﻿#include "StdAfx.h"
#include <math.h>

namespace DuiLib {
#define HSLMAX   255	/* H,L, and S vary over 0-HSLMAX */
#define RGBMAX   255    /* R,G, and B vary over 0-RGBMAX */
#define HSLUNDEFINED (HSLMAX*2/3)

	/*
	* Convert hue value to RGB
	*/
	static float HueToRGB (float v1, float v2, float vH) {
		if (vH < 0.0f) vH += 1.0f;
		if (vH > 1.0f) vH -= 1.0f;
		if ((6.0f * vH) < 1.0f) return (v1 + (v2 - v1) * 6.0f * vH);
		if ((2.0f * vH) < 1.0f) return (v2);
		if ((3.0f * vH) < 2.0f) return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
		return (v1);
	}

	/*
	* Convert color RGB to HSL
	* pHue HSL hue value			[0 - 1]
	* pSat HSL saturation value		[0 - 1]
	* pLue HSL luminance value		[0 - 1]
	*/

	static void RGBToHSL (DWORD clr, float *pHue, float *pSat, float *pLue) {
		float R = (float) (GetRValue (clr) / 255.0f);  //RGB from 0 to 255
		float G = (float) (GetGValue (clr) / 255.0f);
		float B = (float) (GetBValue (clr) / 255.0f);

		float fMin = min (R, min (G, B));		//Min. value of RGB
		float fMax = max (R, max (G, B));		//Max. value of RGB
		float fDelta = fMax - fMin;				//Delta RGB value

		float H = 0.0f, S = 0.0f, L = (fMax + fMin) / 2.0f;

		if (fDelta != 0) {                              
			//Chromatic data...
			float del_R, del_G, del_B;

			if (L < 0.5) S = fDelta / (fMax + fMin);
			else           S = fDelta / (2.0f - fMax - fMin);

			del_R = (((fMax - R) / 6.0f) + (fDelta / 2.0f)) / fDelta;
			del_G = (((fMax - G) / 6.0f) + (fDelta / 2.0f)) / fDelta;
			del_B = (((fMax - B) / 6.0f) + (fDelta / 2.0f)) / fDelta;

			if (R == fMax) H = del_B - del_G;
			else if (G == fMax) H = (1.0f / 3.0f) + del_R - del_B;
			else if (B == fMax) H = (2.0f / 3.0f) + del_G - del_R;

			if (H < 0.0f) H += 1.0f;
			if (H > 1.0f)  H -= 1.0f;
		}

		*pHue = H;
		*pSat = S;
		*pLue = L;
	}

	/*
	* Convert color HSL to RGB
	* H HSL hue value				[0 - 1]
	* S HSL saturation value		[0 - 1]
	* L HSL luminance value			[0 - 1]
	*/
	static DWORD HSLToRGB (float H, float S, float L) {
		BYTE R, G, B;

		if (S == 0) {
			//HSL from 0 to 1
			R = G = B = (BYTE) (L * 255.0f);   //RGB results from 0 to 255
		} else {
			float var_2 = (L < 0.5 ? (L * (1.0f + S)) : ((L + S) - (S * L)));
			float var_1 = (2.0f * L - var_2);

			R = (BYTE) (255.0f * HueToRGB (var_1, var_2, H + (1.0f / 3.0f)));
			G = (BYTE) (255.0f * HueToRGB (var_1, var_2, H));
			B = (BYTE) (255.0f * HueToRGB (var_1, var_2, H - (1.0f / 3.0f)));
		}

		return RGB (R, G, B);
	}

	/*
	* _HSLToRGB color HSL value to RGB
	* clr  RGB color value
	* nHue HSL hue value			[0 - 360]
	* nSat HSL saturation value		[0 - 200]
	* nLue HSL luminance value		[0 - 200]
	*/
#define _HSLToRGB(h,s,l) (0xFF << 24 | HSLToRGB((float)h / 360.0f,(float)s / 200.0f,l / 200.0f))

	///////////////////////////////////////////////////////////////////////
	//
	//
	IMPLEMENT_DUICONTROL (CColorPaletteUI)

		CColorPaletteUI::CColorPaletteUI () {}

	CColorPaletteUI::~CColorPaletteUI () {
		if (m_pBits) free (m_pBits);

		if (m_hMemBitmap) {
			::DeleteObject (m_hMemBitmap);
		}

	}

	DWORD CColorPaletteUI::GetSelectColor () {
		DWORD dwColor = _HSLToRGB (m_nCurH, m_nCurS, m_nCurB);
		return 0xFF << 24 | GetRValue (dwColor) << 16 | GetGValue (dwColor) << 8 | GetBValue (dwColor);
	}

	void CColorPaletteUI::SetSelectColor (DWORD dwColor) {
		float H = 0, S = 0, B = 0;
		COLORREF dwBkClr = RGB (GetBValue (dwColor), GetGValue (dwColor), GetRValue (dwColor));
		RGBToHSL (dwBkClr, &H, &S, &B);
		m_nCurH = (int) (H * 360);
		m_nCurS = (int) (S * 200);
		m_nCurB = (int) (B * 200);
		UpdatePalletData ();
		NeedUpdate ();
	}

	faw::string_view_t CColorPaletteUI::GetClass () const {
		return _T ("ColorPaletteUI");
	}

	LPVOID CColorPaletteUI::GetInterface (faw::string_view_t pstrName) {
		if (pstrName == DUI_CTRL_COLORPALETTE) return static_cast<CColorPaletteUI*>(this);
		return CControlUI::GetInterface (pstrName);
	}

	void CColorPaletteUI::SetPalletHeight (int nHeight) {
		m_nPalletHeight = nHeight;
	}
	int	 CColorPaletteUI::GetPalletHeight () const {
		return m_nPalletHeight;
	}
	void CColorPaletteUI::SetBarHeight (int nHeight) {
		if (nHeight > 150) {
			nHeight = 150; //限制最大高度，由于当前设计，nheight超出190，程序会因越界访问崩溃
		}
		m_nBarHeight = nHeight;
	}
	int  CColorPaletteUI::GetBarHeight () const {
		return m_nBarHeight;
	}

	void CColorPaletteUI::SetThumbImage (faw::string_view_t pszImage) {
		if (m_strThumbImage != pszImage) {
			m_strThumbImage = pszImage;
			NeedUpdate ();
		}
	}

	faw::string_view_t CColorPaletteUI::GetThumbImage () const {
		return m_strThumbImage.str_view ();
	}

	void CColorPaletteUI::SetAttribute (faw::string_view_t pstrName, faw::string_view_t pstrValue) {
		if (pstrName == _T ("palletheight")) SetPalletHeight (_ttoi (pstrValue.data ()));
		else if (pstrName == _T ("barheight")) SetBarHeight (_ttoi (pstrValue.data ()));
		else if (pstrName == _T ("thumbimage")) SetThumbImage (pstrValue);
		else CControlUI::SetAttribute (pstrName, pstrValue);
	}

	void CColorPaletteUI::DoInit () {
		m_MemDc = CreateCompatibleDC (GetManager ()->GetPaintDC ());
		m_hMemBitmap = CreateCompatibleBitmap (GetManager ()->GetPaintDC (), 400, 360);
		SelectObject (m_MemDc, m_hMemBitmap);

		::GetObject (m_hMemBitmap, sizeof (m_bmInfo), &m_bmInfo);
		DWORD dwSize = m_bmInfo.bmHeight * m_bmInfo.bmWidthBytes;
		m_pBits = (BYTE *) malloc (dwSize);
		::GetBitmapBits (m_hMemBitmap, dwSize, m_pBits);
	}

	void CColorPaletteUI::SetPos (RECT rc, bool bNeedInvalidate) {
		CControlUI::SetPos (rc, bNeedInvalidate);

		m_ptLastPalletMouse.x = m_nCurH * (m_rcItem.right - m_rcItem.left) / 360 + m_rcItem.left;
		m_ptLastPalletMouse.y = (200 - m_nCurB) * m_nPalletHeight / 200 + m_rcItem.top;

		UpdatePalletData ();
		UpdateBarData ();
	}

	void CColorPaletteUI::DoEvent (TEventUI& event) {
		CControlUI::DoEvent (event);

		if (event.Type == UIEVENT_BUTTONDOWN) {
			if (event.ptMouse.x >= m_rcItem.left && event.ptMouse.y >= m_rcItem.top &&
				event.ptMouse.x < m_rcItem.right && event.ptMouse.y < m_rcItem.top + m_nPalletHeight) {
				int x = (event.ptMouse.x - m_rcItem.left) * 360 / (m_rcItem.right - m_rcItem.left);
				int y = (event.ptMouse.y - m_rcItem.top) * 200 / m_nPalletHeight;
				x = min (max (x, 0), 360);
				y = min (max (y, 0), 200);

				m_ptLastPalletMouse = event.ptMouse;
				if (m_ptLastPalletMouse.x < m_rcItem.left) m_ptLastPalletMouse.x = m_rcItem.left;
				if (m_ptLastPalletMouse.x > m_rcItem.right) m_ptLastPalletMouse.x = m_rcItem.right;
				if (m_ptLastPalletMouse.y < m_rcItem.top) m_ptLastPalletMouse.y = m_rcItem.top;
				if (m_ptLastPalletMouse.y > m_rcItem.top + m_nPalletHeight) m_ptLastPalletMouse.y = m_rcItem.top + m_nPalletHeight;

				m_nCurH = x;
				m_nCurB = 200 - y;

				m_uButtonState |= UISTATE_PUSHED;
				m_bIsInPallet = true;
				m_bIsInBar = false;

				UpdateBarData ();
			}

			if (event.ptMouse.x >= m_rcItem.left && event.ptMouse.y >= m_rcItem.bottom - m_nBarHeight &&
				event.ptMouse.x < m_rcItem.right && event.ptMouse.y < m_rcItem.bottom) {
				m_nCurS = (event.ptMouse.x - m_rcItem.left) * 200 / (m_rcItem.right - m_rcItem.left);
				m_uButtonState |= UISTATE_PUSHED;
				m_bIsInBar = true;
				m_bIsInPallet = false;
				UpdatePalletData ();
			}

			Invalidate ();
			return;
		}
		if (event.Type == UIEVENT_BUTTONUP) {
			if ((m_uButtonState | UISTATE_PUSHED) && (IsEnabled ())) {
				m_pManager->SendNotify (this, DUI_MSGTYPE_COLORCHANGED, GetSelectColor (), 0);
			}

			m_uButtonState &= ~UISTATE_PUSHED;
			m_bIsInPallet = false;
			m_bIsInBar = false;

			Invalidate ();
			return;
		}
		if (event.Type == UIEVENT_MOUSEMOVE) {
			if (!(m_uButtonState &UISTATE_PUSHED)) {
				m_bIsInBar = false;
				m_bIsInPallet = false;
			}
			if (m_bIsInPallet == true) {
				POINT pt = event.ptMouse;
				pt.x -= m_rcItem.left;
				pt.y -= m_rcItem.top;

				if (pt.x >= 0 && pt.y >= 0 && pt.x <= m_rcItem.right && pt.y <= m_rcItem.top + m_nPalletHeight) {
					int x = pt.x * 360 / (m_rcItem.right - m_rcItem.left);
					int y = pt.y * 200 / m_nPalletHeight;
					x = min (max (x, 0), 360);
					y = min (max (y, 0), 200);

					m_ptLastPalletMouse = event.ptMouse;
					if (m_ptLastPalletMouse.x < m_rcItem.left) m_ptLastPalletMouse.x = m_rcItem.left;
					if (m_ptLastPalletMouse.x > m_rcItem.right) m_ptLastPalletMouse.x = m_rcItem.right;
					if (m_ptLastPalletMouse.y < m_rcItem.top) m_ptLastPalletMouse.y = m_rcItem.top;
					if (m_ptLastPalletMouse.y >= m_rcItem.top + m_nPalletHeight) m_ptLastPalletMouse.y = m_rcItem.top + m_nPalletHeight;

					m_nCurH = x;
					m_nCurB = 200 - y;

					UpdateBarData ();
				}
			} else if (m_bIsInBar == true) {
				m_nCurS = (event.ptMouse.x - m_rcItem.left) * 200 / (m_rcItem.right - m_rcItem.left);
				m_nCurS = min (max (m_nCurS, 0), 200);
				UpdatePalletData ();
			}

			Invalidate ();
			return;
		}

	}

	void CColorPaletteUI::PaintBkColor (HDC hDC) {
		PaintPallet (hDC);
	}

	void CColorPaletteUI::PaintPallet (HDC hDC) {
		int nSaveDC = ::SaveDC (hDC);

		::SetStretchBltMode (hDC, HALFTONE);
		//拉伸模式将内存图画到控件上
		StretchBlt (hDC, m_rcItem.left, m_rcItem.top, m_rcItem.right - m_rcItem.left, m_nPalletHeight, m_MemDc, 0, 1, 360, 200, SRCCOPY);
		StretchBlt (hDC, m_rcItem.left, m_rcItem.bottom - m_nBarHeight, m_rcItem.right - m_rcItem.left, m_nBarHeight, m_MemDc, 0, 210, 200, m_nBarHeight, SRCCOPY);

		RECT rcCurSorPaint = { m_ptLastPalletMouse.x - 4, m_ptLastPalletMouse.y - 4, m_ptLastPalletMouse.x + 4, m_ptLastPalletMouse.y + 4 };
		CRenderEngine::DrawImageString (hDC, m_pManager, rcCurSorPaint, m_rcPaint, m_strThumbImage.str_view ());

		rcCurSorPaint.left = m_rcItem.left + m_nCurS * (m_rcItem.right - m_rcItem.left) / 200 - 4;
		rcCurSorPaint.right = m_rcItem.left + m_nCurS * (m_rcItem.right - m_rcItem.left) / 200 + 4;
		rcCurSorPaint.top = m_rcItem.bottom - m_nBarHeight / 2 - 4;
		rcCurSorPaint.bottom = m_rcItem.bottom - m_nBarHeight / 2 + 4;
		CRenderEngine::DrawImageString (hDC, m_pManager, rcCurSorPaint, m_rcPaint, m_strThumbImage.str_view ());
		::RestoreDC (hDC, nSaveDC);
	}

	void CColorPaletteUI::UpdatePalletData () {
		for (int y = 0; y < 200; ++y) {
			for (int x = 0; x < 360; ++x) {
				BYTE *pPiexl = LPBYTE (m_pBits) + ((200 - y)*m_bmInfo.bmWidthBytes) + ((x*m_bmInfo.bmBitsPixel) / 8);
				DWORD dwColor = _HSLToRGB (x, m_nCurS, y);
				if (dwColor == 0xFF000000) dwColor = 0xFF000001;
				pPiexl[0] = GetBValue (dwColor);
				pPiexl[1] = GetGValue (dwColor);
				pPiexl[2] = GetRValue (dwColor);
			}
		}

		SetBitmapBits (m_hMemBitmap, m_bmInfo.bmWidthBytes * m_bmInfo.bmHeight, m_pBits);
	}


	void CColorPaletteUI::UpdateBarData () {
		//这里画出Bar
		for (int y = 0; y < m_nBarHeight; ++y) {
			for (int x = 0; x < 200; ++x) {
				BYTE *pPiexl = LPBYTE (m_pBits) + ((210 + y)*m_bmInfo.bmWidthBytes) + ((x*m_bmInfo.bmBitsPixel) / 8);
				DWORD dwColor = _HSLToRGB (m_nCurH, x, m_nCurB);
				if (dwColor == 0xFF000000) dwColor = 0xFF000001;
				pPiexl[0] = GetBValue (dwColor);
				pPiexl[1] = GetGValue (dwColor);
				pPiexl[2] = GetRValue (dwColor);
			}
		}

		SetBitmapBits (m_hMemBitmap, m_bmInfo.bmWidthBytes * m_bmInfo.bmHeight, m_pBits);
	}

}